/*
 * Copyright (c) 2015. For Intelligent Group.
 */

package com.intelligent.ispc.core.service.impl;

import com.google.common.collect.Lists;
import com.intelligent.ispc.common.mapper.BeanMapper;
import com.intelligent.ispc.common.persistence.DynamicSpecifications;
import com.intelligent.ispc.common.persistence.SearchFilter;
import com.intelligent.ispc.common.utils.ApplicationException;
import com.intelligent.ispc.common.utils.ExceptionMessages;
import com.intelligent.ispc.core.dto.RoleDto;
import com.intelligent.ispc.core.dto.TestFileDto;
import com.intelligent.ispc.core.dto.UserDto;
import com.intelligent.ispc.core.entity.ItemName;
import com.intelligent.ispc.core.entity.TestFile;
import com.intelligent.ispc.core.entity.TestItemName;
import com.intelligent.ispc.core.repository.jpa.TestFileDao;
import com.intelligent.ispc.core.repository.mongo.TestDataDao;
import com.intelligent.ispc.core.service.SecurityService;
import com.intelligent.ispc.core.service.TestFileService;
import com.intelligent.ispc.utils.Constant;
import com.intelligent.ispc.utils.DataSourceType;
import com.intelligent.ispc.utils.SystemConfig;
import com.intelligent.ispc.utils.TestFileStatus;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Created by Julia.Zhou on 2016/03/12.
 */
@Service
public class TestFileServiceImpl implements TestFileService {
    private static Logger logger = LoggerFactory.getLogger(TestFileServiceImpl.class);

    @Autowired
    private TestDataDao testDataDao;

    @Autowired
    private TestFileDao testFileDao;

    @Autowired
    private SecurityService securityService;

    @Autowired
    private SystemConfig systemConfig;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public Long saveTestFile(TestFileDto testFileDto) {
        if (testFileDto == null || testFileDto.getTestFileUUID() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        TestFile testFile = testFileDao.findByTestFileUUID(testFileDto.getTestFileUUID());
        if (testFile != null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }

        testFile = new TestFile();
        BeanMapper.copy(testFileDto, testFile);
        logger.debug("Saving test file:{}", testFileDto);
        testFileDao.save(testFile);
        logger.info("Save done, file={}", testFile);
        return testFile.getId();
    }

    @Override
    public void updateTestFileStatus(String uuid, TestFileStatus status) {
        if (StringUtils.isBlank(uuid) || StringUtils.isBlank(status.getCode())) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        TestFile testFile = testFileDao.findByTestFileUUID(uuid);
        logger.debug("Updating test file status:{}", testFile.getStatus().getCode());

        testFile.setStatus(status);
        testFile.setUpdateTime(new Date());
        testFileDao.save(testFile);
        logger.debug("Update done, test file status={}", testFile.getStatus().getCode());
    }

    @Override
    public Page<TestFileDto> searchTestFile(Map<String, Object> searchParams, Pageable pageable) {
        String currentName = securityService.getCurrentUserLoginName();
        UserDto userDto = securityService.findByLoginName(currentName);
        boolean isAdmin = false;
        if (userDto != null && userDto.getRoles() != null && !userDto.getRoles().isEmpty()) {
            for (RoleDto role : userDto.getRoles()) {
                if (Constant.ROLE_ENABLE.equals(role.getEnabled()) && Constant.ROLE_ADMINISTRATOR.equals(role.getName())) {
                    isAdmin = true;
                    break;
                }
            }
        }
        logger.debug("Searching test file with {},{},{}", searchParams.keySet(), searchParams.values(), pageable);
        searchParams.put("EQ_dataType", DataSourceType.SPC.getCode());
        Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
        Specification<TestFile> spec = DynamicSpecifications.bySearchFilter(filters.values(), TestFile.class);

        Page<TestFile> testFilePage = testFileDao.findAll(spec, pageable);
        logger.debug("Searching test file, total elements:{}", testFilePage.getTotalElements());

        List<TestFile> testFiles = testFilePage.getContent();
        List<TestFileDto> testFileDtos = Lists.newArrayList();
        for (TestFile testFile : testFiles) {
            TestFileDto testFileDto = new TestFileDto();

            BeanMapper.copy(testFile, testFileDto);
            if (isAdmin || testFile.getCreateBy().equals(currentName)) {
                testFileDto.setHasPermission(true);
            }
            testFileDtos.add(testFileDto);
        }

        Page<TestFileDto> pageDto = new PageImpl<TestFileDto>(testFileDtos, pageable, testFilePage.getTotalElements());

        return pageDto;
    }

    @Override
    public void deleteTestFile(String uuid) {
        if (com.intelligent.ispc.common.utils.StringUtils.isBlank(uuid)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        logger.debug("Delete test File: {}", uuid);
        TestFile testFile = testFileDao.findByTestFileUUID(uuid);
        if (testFile == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        String projectId = testFile.getProject().getId().toString();
        String id = testFile.getId().toString();

        checkPermissionForTestFile(testFile);
        logger.info("Delete test file: originalName={}, project={}, uuid={}, operator={}", testFile.getOriginalName(), testFile.getProject().getName(), uuid, testFile.getCreateBy());

        testFileDao.delete(testFile);
    }

    @Override
    public void destroyTestFileAndTestItemName(String uuid) {
        if (com.intelligent.ispc.common.utils.StringUtils.isBlank(uuid)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        logger.debug("Delete test File: {}", uuid);
        TestFile testFile = testFileDao.findByTestFileUUID(uuid);
        if (testFile == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        String projectId = testFile.getProject().getId().toString();
        String id = testFile.getId().toString();

        checkPermissionForTestFile(testFile);
        logger.info("Delete test file: originalName={}, project={}, uuid={}, operator={}", testFile.getOriginalName(), testFile.getProject().getName(), uuid, testFile.getCreateBy());

        testFileDao.delete(testFile);
        setTestItemName(projectId, id, true, true);
    }


    @Override
    public void logicDeleteTestFile(String uuid) {
        if (com.intelligent.ispc.common.utils.StringUtils.isBlank(uuid)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        logger.debug("Logic delete test file: {}", uuid);
        TestFile testFile = testFileDao.findByTestFileUUID(uuid);
        if (testFile == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        checkPermissionForTestFile(testFile);

        if (testFile.getStatus() == null || TestFileStatus.PROCESSING.equals(testFile.getStatus())) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_TEST_FILE_STATUS_UNDONE);
        }

        if (testFile.getDelete()) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_TEST_FILE_DELETE_STATUS_ERROR);
        }

        testFile.setDelete(true);
        testFile.setCreateBy(securityService.getCurrentUserLoginName());
        testFile.setUpdateTime(new Date());

        Map<String, Object> searchMap = new HashMap<>();
        searchMap.put("testFileUUID", uuid);
        searchMap.put("isDelete", false);
        searchMap.put("projectId", testFile.getProject().getId().toString());

        Map<String, Object> updateMap = new HashMap<>();
        updateMap.put("isDelete", true);

        testDataDao.updateTestData(searchMap, updateMap);
        logger.info("Logic delete test file: originalName={}, project={}, uuid={}, operator={}", testFile.getOriginalName(), testFile.getProject().getName(), uuid, testFile.getCreateBy());
        testFileDao.save(testFile);
        setTestItemName(testFile.getProject().getId().toString(), testFile.getId().toString(), true, false);
    }

    @Override
    public void restoreTestFile(String uuid) {
        if (com.intelligent.ispc.common.utils.StringUtils.isBlank(uuid)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        logger.debug("Checking TestFile: {}", uuid);
        TestFile testFile = testFileDao.findByTestFileUUID(uuid);
        if (testFile == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        checkPermissionForTestFile(testFile);
        List<TestFile> testFileList = testFileDao.findByOriginalNameAndProjectIdAndStatusAndDataType(testFile.getOriginalName(), testFile.getProject().getId(), TestFileStatus.PROCESSING, DataSourceType.SPC.getCode());
        if (testFileList != null && !testFileList.isEmpty()) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_TEST_FILE_STATUS_UNDONE);
        }

        if (!testFile.getDelete()) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_TEST_FILE_DELETE_STATUS_ERROR);
        }

        List<TestFile> normalTestFileList = testFileDao.findByOriginalNameAndProjectIdAndIsDeleteAndDataType(testFile.getOriginalName(), testFile.getProject().getId(), false, DataSourceType.SPC.getCode());
        if (normalTestFileList != null && !normalTestFileList.isEmpty()) {
            TestFile normalTestFile = normalTestFileList.get(0);
            normalTestFile.setDelete(true);
            normalTestFile.setCreateBy(securityService.getCurrentUserLoginName());
            normalTestFile.setUpdateTime(new Date());

            Map<String, Object> searchMap = new HashMap<>();
            searchMap.put("testFileUUID", normalTestFile.getTestFileUUID());
            searchMap.put("isDelete", false);
            searchMap.put("projectId", testFile.getProject().getId().toString());


            Map<String, Object> updateMap = new HashMap<>();
            updateMap.put("isDelete", true);

            testDataDao.updateTestData(searchMap, updateMap);
            testFileDao.save(normalTestFile);
            logger.info("Logic delete test file: originalName={}, project={}, uuid={}, operator={}", testFile.getOriginalName(), testFile.getProject().getName(), uuid, testFile.getCreateBy());
        }

        testFile.setDelete(false);
        testFile.setCreateBy(securityService.getCurrentUserLoginName());
        testFile.setUpdateTime(new Date());

        Map<String, Object> searchMap1 = new HashMap<>();
        searchMap1.put("testFileUUID", uuid);
        searchMap1.put("isDelete", true);
        searchMap1.put("projectId", testFile.getProject().getId().toString());


        Map<String, Object> updateMap1 = new HashMap<>();
        updateMap1.put("isDelete", false);

        testDataDao.updateTestData(searchMap1, updateMap1);
        testFileDao.save(testFile);
        logger.info("Restore test file: originalName={}, project={}, uuid={}, operator={}", testFile.getOriginalName(), testFile.getProject().getName(), uuid, testFile.getCreateBy());
        setTestItemName(testFile.getProject().getId().toString(), testFile.getId().toString(), false, false);
    }

    @Override
    public boolean checkTestFile(List<String> originalNames, String projectId) {
        if (originalNames == null || originalNames.isEmpty() || StringUtils.isBlank(projectId) || !StringUtils.isNumeric(projectId)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        boolean checkFile = false;
        logger.debug("Checking TestFile: {}, {}", originalNames, projectId);
        List<TestFile> testFileList = testFileDao.findByOriginalNameInAndProjectIdAndIsDeleteAndDataType(originalNames, Long.valueOf(projectId), false, DataSourceType.SPC.getCode());
        if (testFileList != null && !testFileList.isEmpty()) {
            for (TestFile testFile : testFileList) {
                if (!testFile.getDelete()) {
                    checkFile = true;
                    break;
                }
            }
        }

        return checkFile;
    }

    private void checkPermissionForTestFile(TestFile testFile) {
        String currentName = securityService.getCurrentUserLoginName();
        UserDto userDto = securityService.findByLoginName(currentName);
        if (userDto != null && userDto.getRoles() != null && !userDto.getRoles().isEmpty()) {
            boolean isAdmin = false;
            for (RoleDto role : userDto.getRoles()) {
                if (Constant.ROLE_ENABLE.equals(role.getEnabled()) && Constant.ROLE_ADMINISTRATOR.equals(role.getName())) {
                    isAdmin = true;
                    break;
                }
            }
            if (!isAdmin && !currentName.equals(testFile.getCreateBy())) {
                throw new ApplicationException(ExceptionMessages.EXCEPTION_NO_PERMISSION);
            }
        }
    }

    @Override
    public void setTestItemName(String projectId, String fileId, boolean isLogicDelete, boolean isDelete) {
        TestItemName testItemName1 = new TestItemName();
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and("projectId").is(projectId);
        query.addCriteria(criteria);
        TestItemName testItemName = mongoTemplate.findAndRemove(query, TestItemName.class);
        List<String> deletedFileIds = null;
        List<String> fileIds = null;
        if (testItemName != null && testItemName.getItemNames() != null) {
            ListIterator lit = testItemName.getItemNames().listIterator();
            while (lit.hasNext()) {
                ItemName name = (ItemName) lit.next();
                lit.remove();
                ItemName itemName = new ItemName();
                itemName.setName(name.getName());
                if (isLogicDelete) {
                    deletedFileIds = Lists.newArrayList();
                    //logicDelete
                    if (!isDelete && name.getFileIds() != null) {
                        ListIterator ids = name.getFileIds().listIterator();
                        while (ids.hasNext()) {
                            String id = (String) ids.next();
                            if (fileId.equals(id)) {
                                ids.remove();
                                deletedFileIds.add(fileId);
                                break;
                            }
                        }
                        if (name.getDeletedFileIds() != null) {
                            deletedFileIds.addAll(name.getDeletedFileIds());
                        }
                        itemName.setDeletedFileIds(deletedFileIds);
                        itemName.setFileIds(name.getFileIds());
                    }
                    //delete
                    if (isDelete && name.getDeletedFileIds() != null) {
                        ListIterator ids = name.getDeletedFileIds().listIterator();
                        while (ids.hasNext()) {
                            String id = (String) ids.next();
                            if (fileId.equals(id) && isDelete) {
                                ids.remove();
                                break;
                            }
                        }
                        itemName.setDeletedFileIds(name.getDeletedFileIds());
                        itemName.setFileIds(name.getFileIds());
                    }

                } else {
                    fileIds = Lists.newArrayList();
                    //restore
                    if (name.getDeletedFileIds() != null) {
                        ListIterator deletedIds = name.getDeletedFileIds().listIterator();
                        while (deletedIds.hasNext()) {
                            String id = (String) deletedIds.next();
                            if (fileId.equals(id) && !isDelete) {
                                fileIds.add(fileId);
                                deletedIds.remove();
                                break;
                            }
                        }
                    }
                    itemName.setDeletedFileIds(name.getDeletedFileIds());
                    if (name.getFileIds() != null) {
                        fileIds.addAll(name.getFileIds());
                    }
                    itemName.setFileIds(fileIds);
                }
                itemName.setNumeric(name.getNumeric());
                lit.add(itemName);
            }
            testItemName1.setProjectId(projectId);
            testItemName1.setItemNames(testItemName.getItemNames());
            mongoTemplate.insert(testItemName1);
        }
    }

}
